// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package math

/*
	Floating-point tangent.
*/

// 下面的原始C代码、长注释和常量
// 来自http:
// 可从http:
// go代码是原始C.
// 
// tan.C 
// 
// 圆切线
// 
// 简介：
// 
// double x，y，tan；
// y=tan（x）；
// 
// 说明：
// 
// 返回弧度参数x的圆切线。
// 
// 范围缩减为π/4模。在基本区间[0，pi/4]中使用了有理函数
// x+x**3p（x**2）/Q（x**2）
// 。
// 
// 准确度：
// 相对误差：
// 算术域#试验峰值均方根值
// DEC+-1.07e9 44000 4.1e-17 1.0e-17 
// 在x=2**30=1.074e9时开始出现部分精度损失。
// 的损失不是渐进的，而是突然跳到10e7的1分左右。对于x>2**49=5.6e14，结果可能毫无意义。
// /[来自sin.go评论的准确度损失声明]
// /
// /Cephes数学库发行版2.8:2000年6月
// /版权所有1984、1987、1989、1992，2000年由Stephen L.Moshier 
// 
// 自述文件位于http:
// 此档案中的某些软件可能来自《数学函数的方法和程序》（Prentice Hall或Simon&Schuster 
// 国际，1989）一书或者从Cephes数学库，
// 商业产品。无论哪种情况，它都是作者的版权。
// 您在这里看到的内容可以免费使用，但没有任何支持或
// 保证。
// 
// 本书中两个已知的印刷错误在这里的
// 伽马函数和不完整的beta 
// 积分的源代码列表中修复。
// 
// Stephen L.Moshier 
// /moshier@na-net.ornl.gov 

// tan系数
var _tanP = [...]float64{
	-1.30936939181383777646e4, // 0xC0C992D8D24F38 
	1.15351664838587416140e6,  // 0x413199eca5fc9ddd 
	-1.79565251976484877988e7, // 0xC1711EAD3299176 
}
var _tanQ = [...]float64{
	1.00000000000000000000e0,
	2.50083801823357915839e7,  // 0x4177d98fc2ead8ef 
	-5.38695755929454629881e7, // 0xc189afe03cbe5a31 
}

// Tan返回弧度参数x的正切值。
// 
// 特殊情况为：
// Tan（±0）=±0 
// Tan（±Inf）=NaN 
// Tan（NaN）=NaN 
func Tan(x float64) float64 {
	if haveArchTan {
		return archTan(x)
	}
	return tan(x)
}

func tan(x float64) float64 {
	const (
		PI4A = 7.85398125648498535156e-1  // 0x3FE921FB400000，Pi/4分为三部分
		PI4B = 3.77489470793079817668e-8  // 0x3E6442D00000000，
	)
	// 特殊情况
	switch {
	case x == 0 || IsNaN(x):
		return x // 返回±0 | | NaN（）
	case IsInf(x, 0):
		return NaN()
	}

	// 使参数为正，但保存符号
	sign := false
	if x < 0 {
		x = -x
		sign = true
	}
	var j uint64
	var y, z float64
	if x >= reduceThreshold {
		j, z = trigReduce(x)
	} else {
		j = uint64(x * (4 / Pi)) // x的整数部分/（Pi/4），作为相位角测试的整数
		y = float64(j)           // x的整数部分/（Pi/4），作为浮点

		/* map zeros and singularities to origin */
		if j&1 == 1 {
			j++
			y++
		}

		z = ((x - y*PI4A) - y*PI4B) - y*PI4C
	}
	zz := z * z

	if zz > 1e-14 {
		y = z + z*(zz*(((_tanP[0]*zz)+_tanP[1])*zz+_tanP[2])/((((zz+_tanQ[1])*zz+_tanQ[2])*zz+_tanQ[3])*zz+_tanQ[4]))
	} else {
		y = z
	}
	if j&2 == 2 {
		y = -1 / y
	}
	if sign {
		y = -y
	}
	return y
}
